#pragma once
#include <string>
#include <iostream>
#include <unistd.h>
#include <sys/types.h>
#include <ctime>
#include <cstdarg>
#include <fstream>
#include <cstring>
#include "LockGuard.hpp"
using std::cout;
using std::endl;

namespace log_ddsm
{
    // 日志信息管理
    enum LEVEL
    {
        DEBUG = 1,   // 调试信息
        INFO = 2,    // 提示信息
        WARNING = 3, // 警告
        ERROR = 4,   // 错误，但是不影响服务正常运行
        FATAL = 5,   // 致命错误，服务无法正常运行
    };
    enum 
    {
        TIMESIZE = 128,
        LOGSIZE = 1024,

        FILE_TYPE_LOG_SIZE = 2048
    };
    enum 
    {
        SCREEN_TYPE = 8,
        FILE_TYPE = 16
    };

    // 默认日志文件名称
    const char *DEFAULT_LOG_NAME = "./log.txt";
    // 全局锁，保护打印日志
    pthread_mutex_t gmutex = PTHREAD_MUTEX_INITIALIZER;

    struct logMessage
    {
        std::string _level; // 日志等级
        int _level_num;     // 日志等级的int格式

        pid_t _pid;             // 这条日志的进程id
        std::string _file_name; // 文件名
        int _file_number;       // 行号
        std::string _cur_time;  // 当时的时间
        std::string _log_info;  // 日志正文
    };

    // 通过int获取日志等级
    std::string getLevel(int level)
    {
        switch (level)
        {
        case 1:
            return "DEBUG";
        case 2:
            return "INFO";
        case 3:
            return "WARNING";
        case 4:
            return "ERROR";
        case 5:
            return "FATAL";
        default:
            return "UNKNOWN";
        }
    }

    std::string getCurTime()
    {
        time_t cur = time(nullptr);
        struct tm *curtime = localtime(&cur);
        char buf[TIMESIZE] = {0};
        snprintf(buf, sizeof(buf), "%d-%d-%d %d:%d:%d",
                 curtime->tm_year + 1900,
                 curtime->tm_mon + 1,
                 curtime->tm_mday,
                 curtime->tm_hour,
                 curtime->tm_min,
                 curtime->tm_sec);
        return buf;
    }

    class Log
    {
    private:
        void FlushMessage(const logMessage &lg)
        {
            // 互斥锁，保护Print过程
            LockGuard lockguard(&gmutex);

            // 过滤逻辑
            // 致命错误到文件逻辑
            if (lg._level_num >= _ignore_level)
            {
                if (_print_type == SCREEN_TYPE)
                    PrintToScreen(lg);
                else if (_print_type == FILE_TYPE)
                    PrintToFile(lg);
                else
                    std::cerr << __FILE__ << " " << __LINE__ << ":" << " UNKNOWN_TYPE " << std::endl;
            }
        }
        void PrintToScreen(const logMessage &lg)
        {
            printf("[%s][%d][%s][%d][%s] %s", lg._level.c_str(),
                   lg._pid,
                   lg._file_name.c_str(),
                   lg._file_number,
                   lg._cur_time.c_str(),
                   lg._log_info.c_str());
        }

        void PrintToFile(const logMessage &lg)
        {
            std::ofstream out(_log_file_name, std::ios::app); // 追加打印
            if (!out.is_open())
            {
                std::cerr << __FILE__ << " " << __LINE__ << ":" << " LOG_FILE_OPEN fail " << std::endl;
                return;
            }
            char log_txt[FILE_TYPE_LOG_SIZE] = {0}; // 缓冲区
            snprintf(log_txt, sizeof(log_txt), "[%s][%d][%s][%d][%s] %s", lg._level.c_str(),
                     lg._pid,
                     lg._file_name.c_str(),
                     lg._file_number,
                     lg._cur_time.c_str(),
                     lg._log_info.c_str());

            out.write(log_txt, strlen(log_txt)); // 写入文件
        }

    public:
        // 打印方式——默认向显示器打印
        // 如果选择文件类型打印，默认的文件名称是当前目录的log.txt
        //
        Log(int print_type = SCREEN_TYPE)
            : _print_type(print_type), _log_file_name(DEFAULT_LOG_NAME), _ignore_level(DEBUG)
        {
        }

        // 只有一个全局对象，多个构造函数多余,不如设计一个构造，添加一个设置log文件名称的接口
        //  Log(int print_type, const char *logname = DEFAULT_LOG_NAME)
        //      : _print_type(print_type), _log_file_name(logname), _ignore_level(1)
        //  {
        //  }

        void Enable(int type)
        {
            _print_type = type;
        }

        /// @brief 加载日志信息
        /// @param format 格式化输出
        /// @param  可变参数
        /*区分了本层和商城之后，就容易设置参数了*/
        void load_message(int level, std::string filename, int filenumber, const char *format, ...)
        {
            logMessage lg;
            lg._level = getLevel(level);
            lg._level_num = level;

            lg._pid = getpid();
            lg._file_name = filename;
            lg._file_number = filenumber;
            lg._cur_time = getCurTime();

            // valist + vsnprintf 处理可变参数
            va_list ap;
            va_start(ap, format);
            char log_info[LOGSIZE] = {0};
            vsnprintf(log_info, sizeof(log_info), format, ap);
            va_end(ap);
            lg._log_info = log_info;

            // 打印逻辑
            FlushMessage(lg);
        }

        /// @brief
        /// @param ignorelevel
        /* DEBUG = 1,   调试信息
         *INOF = 2,     提示信息
         *WARNING = 3,  警告
         *ERROR = 4,    错误，但是不影响服务正常运行
         *FATAL = 5,    致命错误，服务无法正常运行 */
        void SetIgnoreLevel(int ignorelevel)
        {
            _ignore_level = ignorelevel;
        }
        void SetLogFileName(const char *newlogfilename)
        {
            _log_file_name = newlogfilename;
        }
        ~Log() {}

    private:
        int _print_type;
        std::string _log_file_name;
        int _ignore_level;
    };

    // 全局的Log对象
    Log lg;
    // 打印日志信息
    // e.g.
    // LOG(DEBUG,"this is a test message %d %f %c", 13 , 9.81 , 'A' );
    //

// 使用日志的一般格式
#define LOG(Level, Format, ...)                                            \
    do                                                                     \
    {                                                                      \
        lg.load_message(Level, __FILE__, __LINE__, Format, ##__VA_ARGS__); \
    } while (0)

    /// 无法设计为inlne——__VA_ARGS只能出现在宏替换中
    // inline void LOG(int level,const char* format, ...)
    // {
    //     lg.load_message(level,__FILE__,__LINE__,format,__VA_ARGS__);
    // }

// 往文件打印
#define ENABLE_FILE()         \
    do                        \
    {                         \
        lg.Enable(FILE_TYPE); \
    } while (0)
// 往显示器打印
#define ENABLE_SCREEN()         \
    do                          \
    {                           \
        lg.Enable(SCREEN_TYPE); \
    } while (0)
// 设置日志忽略等级
#define SET_IGNORE_LEVEL(Level)  \
    do                           \
    {                            \
        lg.SetIgnoreLevel(Level) \
    } while (0)
// 设置日志文件名称
#define SET_LOG_FILENAME(Name)  \
    do                          \
    {                           \
        lg.SetLogFileName(Name) \
    } while (0)

}; // namespace log_ddsm
